﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LostLib.Maths.Geometry3D
{
	[Serializable]
	public struct Vector3D: IEquatable<Vector3D>
	{
		#region Member Fields
		public double X, Y, Z;
		#endregion Member Fields

		#region Arithmetics
		public static Vector3D operator +(Vector3D left, Vector3D right)
		{
			return new Vector3D {
				X = left.X + right.X,
				Y = left.Y + right.Y,
				Z = left.Z + right.Z,
			};
		}

		public static Vector3D operator -(Vector3D left, Vector3D right)
		{
			return new Vector3D {
				X = left.X - right.X,
				Y = left.Y - right.Y,
				Z = left.Z - right.Z,
			};
		}

		public static Vector3D operator*(Vector3D vector, double scale)
		{
			return new Vector3D {
				X = vector.X * scale,
				Y = vector.Y * scale,
				Z = vector.Z * scale,
			};
		}

		public static Vector3D operator*(double scale, Vector3D vector)
		{
			return new Vector3D {
				X = vector.X * scale,
				Y = vector.Y * scale,
				Z = vector.Z * scale,
			};
		}

		public static Vector3D operator -(Vector3D vector)
		{
			return new Vector3D {
				X = -vector.X,
				Y = - vector.Y,
				Z = -vector.Z,
			};
		}

		public static Vector3D operator /(Vector3D vector, double scaleInv)
		{
			return vector * (1.0 / scaleInv);
		}

		public static bool operator ==(Vector3D left, Vector3D right)
		{
			return left.X == right.X && left.Y == right.Y && left.Z == right.Z;
		}

		public static bool operator !=(Vector3D left, Vector3D right)
		{
			return left.X != right.X || left.Y != right.Y || left.Z != right.Z;
		}
		#endregion Arithmetics

		#region Special Values
		public bool IsNaN()
		{
			return double.IsNaN(X) || double.IsNaN(Y) || double.IsNaN(Z);
		}

		public bool IsInfinity()
		{
			return !IsNaN() && (double.IsInfinity(X) || double.IsInfinity(Y) || double.IsInfinity(Z));
		}
		#endregion Special Values

		#region Equality
		public bool Equals(Vector3D other)
		{
			return X.Equals(other.X) && Y.Equals(other.Y) && Z.Equals(other.Z);
		}

		public override bool Equals(object other)
		{
			if (other == null) return false;
			if (other.GetType() != typeof(Vector3D)) return false;
			return Equals((Vector3D)other);
		}

		public override int GetHashCode()
		{
			return X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode();
		}
		#endregion Equality

		#region ToString
		public override string ToString()
		{
			return string.Format("{0}, {1}, {2}", X, Y, Z);
		}
		#endregion ToString

		#region Geometry
		public double DotProduct(Vector3D other)
		{
			return X * other.X + Y * other.Y + Z * other.Z;
		}

		public double LengthSquare
		{
			get
			{
				return X * X + Y * Y + Z * Z;
			}
		}

		public double Length
		{
			get
			{
				return Math.Sqrt(LengthSquare);
			}
		}

		public Vector3D CrossProduct(Vector3D other)
		{
			return new Vector3D {
				X = Y * other.Z - Z * other.Y,
				Y = Z * other.X - X * other.Z,
				Z = X * other.Y - Y * other.X,
			};
		}
		#endregion Geometry
	}
}
